package mc.support.task.msg;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created with IntelliJ IDEA. <br/>
 * Author: Francis Yun    <br/>
 * Date: 2014-09-17  <br/>
 */
public class MessageGroup<Offer, Reply> {

    private List<Message<Offer, Reply>> messages;
    private List<Offer> offers;
    private List<Reply> replies;
    private final int msgCount;
    private final AtomicInteger replyCount = new AtomicInteger(0);
    private Set<String> threadNames;
    private boolean statisThreads;

    public MessageGroup(List<Offer> offers) {
        this( offers, false );
    }

    public MessageGroup(List<Offer> offers, boolean statisThreads) {
        this.offers = offers;
        msgCount = offers.size();
        this.statisThreads = statisThreads;
    }

    public List<Message<Offer, Reply>> messages() {
        if ( null == messages ) {
            messages = new ArrayList<>( msgCount );
            for ( Offer offer : offers ) {
                messages.add( new Message<>( this ).offer( offer ) );
            }
        }
        return messages;
    }

    public int msgCount() {
        return msgCount;
    }

    public Set<String> handledThreads() {
        return threadNames;
    }

    public List<Reply> replies() throws InterruptedException {
        if ( null == messages ) return null;
        if ( null == replies ) {
            spinCheck();
            replies = new ArrayList<>( msgCount );
            for ( Message<Offer, Reply> msg : messages ) {
                replies.add( msg.replied() );
            }
        }
        return replies;
    }

    public boolean isAllReplied() {
        return msgCount <= replyCount.get();
    }

    void replyCountUp() {
        if ( msgCount <= replyCount.incrementAndGet() ) {
            synchronized ( this ) {
                this.notifyAll();
            }
        }
        if ( statisThreads ) {
            if ( null == threadNames ) threadNames = new HashSet<>();
            threadNames.add( Thread.currentThread().getName() );
        }
    }

    private void spinCheck() throws InterruptedException {
        synchronized ( this ) {
            while ( ! isAllReplied() ) {
                this.wait();
            }
        }
    }

}
